package com.qhs.framework.config.shiro;

import com.qhs.sys.entity.SysMenuInfo;
import com.qhs.sys.entity.SysRoleInfo;
import com.qhs.sys.entity.SysUserInfo;
import com.qhs.sys.service.MenuService;
import com.qhs.sys.service.RoleService;
import com.qhs.sys.service.UserService;
import com.qhs.sys.vo.UserVO;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.ArrayList;
import java.util.List;

/**
 *
 * <shiro:authenticated> 登录之后
 <shiro:notAuthenticated> 不在登录状态时
 <shiro:guest> 用户在没有RememberMe时
 <shiro:user> 用户在RememberMe时
 <shiro:hasAnyRoles name="abc,123" > 在有abc或者123角色时
 <shiro:hasRole name="abc"> 拥有角色abc
 <shiro:lacksRole name="abc"> 没有角色abc
 <shiro:hasPermission name="abc"> 拥有权限abc
 <shiro:lacksPermission name="abc"> 没有权限abc
 <shiro:principal> 显示用户登录名

 * Created by xinchao on 2017/1/17 0017.
 */
public class ShiroRealm extends AuthorizingRealm {

    @Autowired
    UserService userService;
    @Autowired
    RoleService roleService;
    @Autowired
    MenuService menuService;

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection paramPrincipalCollection) {
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();

        // 因为非正常退出，即没有显式调用 SecurityUtils.getSubject().logout()
        // (可能是关闭浏览器，或超时)，但此时缓存依旧存在(principals)，所以会自己跑到授权方法里。
        if (!SecurityUtils.getSubject().isAuthenticated()) {
            doClearCache(paramPrincipalCollection);
            SecurityUtils.getSubject().logout();
            return null;
        }

        // 页面使用标签来验证是否有资源权限时，使用下面代码
        UserVO user = (UserVO) SecurityUtils.getSubject().getSession().getAttribute("authUser");
        if (user != null) {
            // 当前用户角色编码集合
            List<String> roleIds = new ArrayList<>();

            for (SysRoleInfo role : roleService.findRoleListByUserName(user.getUserName())) {
                roleIds.add(String.valueOf(role.getRoleCode()));
            }
            authorizationInfo.addRoles(roleIds);

            //资源信息
            List<String> resources = new ArrayList<>();
            for (SysMenuInfo resource:menuService.findResourceListByUserId(user.getUserId())){
                resources.add(resource.getMenuCode());
            }
            authorizationInfo.addStringPermissions(resources);
        }
        return authorizationInfo;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken)  throws AuthenticationException {
        //获取用户的输入的账号.
        String username = (String)authenticationToken.getPrincipal();
        //通过username从数据库中查找 User对象，如果找到，没找到.
        //实际项目中，这里可以根据实际情况做缓存，如果不做，Shiro自己也是有时间间隔机制，2分钟内不会重复执行该方法
        SysUserInfo userInfo = userService.findUserByName(username);
        if(userInfo == null){
            return null;
        }
        if(userInfo.getStatus().equals(2)){
            throw  new AuthenticationException("用户已冻结");
        }

        //账号判断;
        //交给AuthenticatingRealm使用CredentialsMatcher进行密码匹配，如果觉得人家的不好可以自定义实现
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
                userInfo, //用户
                userInfo.getPassword(), //密码
                ByteSource.Util.bytes(userInfo.getSalt()),
                getName()  //realm name
        );


        /*
		 * 获取权限信息:
		 */
        Subject currentUser = SecurityUtils.getSubject();
        Session session = currentUser.getSession();
        UserVO userVO = UserVO.clone(userInfo);
        userVO.setAuthMenu(menuService.findMenuListByUserId(userInfo.getId()));
        session.setAttribute("authUser",userVO);

        return authenticationInfo;
    }


}
